package linuxbox.calendar.logic;

import java.sql.*;
import java.util.*;
import java.util.Date;

import org.apache.log4j.*;
import linuxbox.*;
import linuxbox.calendar.bean.*;
import linuxbox.util.*;

public class BoxCalendar {
	protected static Logger logger = Logger.getLogger(BoxCalendar.class
			.getName());

	public static final int BOXCALENDAR_QUERY_BY_TITLE = 1;
	public static final int BOXCALENDAR_QUERY_BY_DESCRIPTION = 2;
	public static final int BOXCALENDAR_PRIVATE = 0;
	public static final int BOXCALENDAR_BUSY = 1;
	public static final int BOXCALENDAR_PUBLIC = 2;

	public static final int BOXCALENDAR_LIST_ALL = 0;
	public static final int BOXCALENDAR_LIST_UPCOMING = 1;
	public static final int BOXCALENDAR_LIST_PAST = 2;

	public static BoxCalendar staticInstance = null;

	public static BoxCalendar getInstance() {
		if (staticInstance == null) {
			staticInstance = new BoxCalendar();
		}
		return staticInstance;
	}

	private BoxCalendar() {
	}

	public BoxCalendarEntry get(int id, int userid) {
		return get(new BoxCalendarEntryKey(id, userid));
	}

	public BoxCalendarEntry get(BoxCalendarEntryKey key) {
		BoxCalendarEntry entry = null;
		try {
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(key);
			bean.load();
			entry = new BoxCalendarEntry();
			entry.copyFrom(bean);
		} catch (Exception e) {
			logger.error(e);
		}
		return entry;
	}

	public BoxCalendarRepeat getRepeat(BoxCalendarEntryKey key) {
		BoxCalendarRepeat entry = null;
		try {
			BoxCalendarRepeatBean bean = new BoxCalendarRepeatBean(key);
			bean.load();
			entry = new BoxCalendarRepeat();
			entry.copyFrom(bean);
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
		return entry;
	}

	public BoxCalendarEntry add(int userid) {
		try {
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(
					new BoxCalendarEntryKey(0, userid));
			bean.create();

			BoxCalendarEntry entry = new BoxCalendarEntry();
			entry.copyFrom(bean);
			return entry;
		} catch (Exception e) {
			logger.error(e);
		}
		return null;
	}

	public boolean remove(BoxCalendarEntryKey key) {
		try {
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(key);
			bean.load();
			if (bean.getRepeat() && bean.getInvitedUserCount() <= 0) {
				BoxCalendarRepeatBean repeat = new BoxCalendarRepeatBean(key);
				repeat.remove();
			}
			bean.remove();
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
		return true;
	}

	public boolean remove(int id, int userid) {
		return remove(new BoxCalendarEntryKey(id, userid));
	}

	public boolean remove(BoxCalendarEntry entry) {
		return remove(entry.getID(), entry.getUserID());
	}

	public boolean update(BoxCalendarEntry entry, BoxCalendarRepeat repeat,
			int[] invitedUsers) {
		try {
			DbAdapter db = new DbAdapter(true);
			BoxCalendarEntryKey key = new BoxCalendarEntryKey(entry.getID(),
					entry.getUserID());
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(key, db);

			if (entry == null) {
				return false;
			}

			bean.copyFrom(entry);
			// If you are not passing the repeat entry, I will set the repeat
			// to false, otherwise, it will look for a repeat bean which is not
			// exist.
			if (repeat == null) {
				bean.setRepeat(false);
			}
			bean.setCreatedBy(bean.getUserID());
			bean.store();

			BoxCalendarRepeatBean repeatBean = new BoxCalendarRepeatBean(key,
					db);
			if (bean.getRepeat()) {
				// The repeat bean's ID has to be the same as the entry
				if (repeatBean.findByKey() == null) {
					repeatBean.create();
				}
				repeatBean.copyFrom(repeat);
				repeatBean.store();
			} else {
				repeatBean.remove();
			}

			// Remove all the old entries, and add the new entries
			StringBuffer sql = new StringBuffer();
			sql.append("DELETE FROM ");
			sql.append(bean.getTableName());
			sql.append(" where id=");
			sql.append(entry.getID());
			sql.append(" and userid<>'");
			sql.append(entry.getUserID());
			sql.append("'");
			bean.updateBySql(sql.toString());

			BoxStatic.logger.debug("Invited users: " + invitedUsers);
			if (invitedUsers != null && invitedUsers.length > 0) {
				int CurUserID = bean.getUserID();
				for (int i = 0; i < invitedUsers.length; i++) {
					BoxStatic.logger.debug("SQL: " + invitedUsers[i]);
					if (CurUserID != invitedUsers[i]) {
						bean.setUserID(invitedUsers[i]);
						bean.create();
						bean.setCreatedBy(CurUserID);
						bean.store();
					}
				}
			}

			db.close();
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
		return true;
	}

	public Collection search(int viewerid, int userid, String searchStr0,
			int searchBy, String searchCategory, String SORTBY,
			boolean sortAsc, int from, int limit) {
		logger.debug(userid + searchStr0 + searchBy + searchCategory);

		StringBuffer sql = new StringBuffer();

		if ((searchBy & (BOXCALENDAR_QUERY_BY_TITLE | BOXCALENDAR_QUERY_BY_DESCRIPTION)) == 0
				&& (searchCategory == null || searchCategory.length() == 0)) {
			return null;
		}
		String searchStr = StringUtil.replace(searchStr0, "\'", "\'\'");
		if ((searchBy & BOXCALENDAR_QUERY_BY_TITLE) != 0) {
			sql.append(" where ( subject like '%" + searchStr + "%'");
		}
		if ((searchBy & BOXCALENDAR_QUERY_BY_DESCRIPTION) != 0) {
			if (sql.length() == 0) {
				sql.append(" where ( description like '%" + searchStr + "%'");
			} else {
				sql.append(" or description like '%" + searchStr + "%'");
			}
		}

		if (sql.length() > 0) {
			sql.append(" ) and userid=" + userid);
		} else {
			sql.append(" where userid=" + userid);

		}
		if (searchCategory != null && searchCategory.length() > 0) {
			sql.append(" and category = '"
					+ StringUtil.replace(searchCategory, "\'", "\'\'") + "'");
		}
		return search(viewerid, userid, sql.toString(), SORTBY, sortAsc, from,
				limit);
	}

	public Collection search(int viewerid, int userid, String sql0,
			String SORTBY, boolean sortAsc, int from, int limit) {
		StringBuffer sql = new StringBuffer();
		sql.append(sql0);
		sql.append(" order by ");
		sql.append(SORTBY);
		if (sortAsc) {
			sql.append(" ASC ");
		} else {
			sql.append(" DESC ");
		}
		DBUtil.appendLimit(sql, from, limit);

		DbAdapter db = null;
		try {
			db = new DbAdapter(true);
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(null, db);
			Iterator it = bean.findBySql(sql.toString());
			ArrayList l = new ArrayList();
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);

					l.add(entry);
					// System.out.println(entry);
				}
			}
			db.close();
			if (l.size() > 0) {
				if (viewerid != userid) {
					filterByViewer(viewerid, l);
				}
			}
			return l;
		} catch (Exception e) {
			logger.error(e);
		}
		return null;
	}

	public int[] getInvitedUsers(BoxCalendarEntry entry) {
		try {
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(
					new BoxCalendarEntryKey(entry.getID(), entry.getUserID()));
			return bean.getInvitedUsers();
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public boolean hasEntryForDay(Calendar curDate, int userid) {
		// Calendar curDate = Calendar.getInstance();
		// curDate.setTime(date);
		trimForDay(curDate);

		try {
			BoxStatic.dateTimeFormatter
					.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
			String strDate = BoxStatic.dateTimeFormatter.format(curDate
					.getTime());
			String sql = " where userid =" + userid + " and fromtime >= '"
					+ strDate + "' and fromtime < '";

			// Set to next day
			curDate.add(Calendar.DATE, 1);
			strDate = BoxStatic.dateTimeFormatter.format(curDate.getTime());
			sql += strDate + "' order by fromtime, id";
			curDate.add(Calendar.DATE, -1);

			// Get the entries from database
			DbAdapter db = new DbAdapter(true);
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(null, db);
			Iterator it = bean.findBySql(sql);
			while (it != null && it.hasNext()) {
				db.close();
				db = null;
				return true;
			}

			// Now check the repeat entries
			sql = " where userid =" + userid + " and repeatflag = 1";
			it = bean.findBySql(sql);
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarRepeatBean repeatBean = new BoxCalendarRepeatBean(
							key, db);
					repeatBean.load();
					BoxCalendarRepeat repeat = new BoxCalendarRepeat();
					repeat.copyFrom(repeatBean);

					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);

					if (isRepeatInDate(entry, repeat, curDate)) {
						db.close();
						db = null;
						return true;
					}
				}
			}
			db.close();
			db = null;
		} catch (Exception e) {
			logger.error(e);
		}
		return false;
	}

	public Iterator getAllDayEntries(int viewerid, Calendar curDate, int userid) {
		// Calendar curDate = Calendar.getInstance();
		// curDate.setTime(date);
		Vector v = null;
		try {
			Enumeration result = null;
			BoxStatic.dateTimeFormatter
					.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
			String strDate = BoxStatic.dateTimeFormatter.format(curDate
					.getTime());
			String sql = " where isallday=1 and userid =" + userid
					+ " and fromtime >= '" + strDate + "' and fromtime < '";

			// Set to next day
			curDate.add(Calendar.DATE, 1);
			strDate = BoxStatic.dateTimeFormatter.format(curDate.getTime());
			sql += strDate + "' and repeatflag = 0 order by fromtime, id";
			curDate.add(Calendar.DATE, -1);

			// Get the entries from database
			v = new Vector();
			DbAdapter db = new DbAdapter(true);
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(null, db);
			Iterator it = bean.findBySql(sql);
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);

					v.addElement(entry);
				}
			}

			// Get the repeat entries
			sql = " where userid =" + userid + " and repeatflag = 1";
			it = bean.findBySql(sql);
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);
					if (entry.getIsAllDay()) // only get allday entries
					{
						BoxCalendarRepeatBean repeatBean = new BoxCalendarRepeatBean(
								key, db);
						repeatBean.load();
						BoxCalendarRepeat repeat = new BoxCalendarRepeat();
						repeat.copyFrom(repeatBean);

						if (isRepeatInDate(entry, repeat, curDate)) {
							insertBeanToSortedVector(entry, v, curDate
									.getTimeZone());
						}
					}
				}
			}
			db.close();
			db = null;
		} catch (Exception e) {
			logger.error(e);
		}
		if (v != null && v.size() > 0) {
			if (viewerid != userid) {
				filterByViewer(viewerid, v);
			}
			return v.iterator();
		} else {
			return null;
		}
	}

	// Will only none allday entries.
	public Iterator getEntries(int viewerid, Calendar curDate, int userid) {
		// Calendar curDate = Calendar.getInstance();
		// curDate.setTime(date);
		Vector v = null;
		try {
			String strDate = StringUtil.getDateTimeFormatString(curDate
					.getTimeZone(), BoxStatic.POSTGRES_DATE_FORMAT, curDate
					.getTime());
			String sql = " where isallday=0 and userid =" + userid
					+ " and fromtime >= '" + strDate + "' and fromtime < '";

			// Set to next day
			curDate.add(Calendar.DATE, 1);
			strDate = BoxStatic.dateTimeFormatter.format(curDate.getTime());
			sql += strDate + "' and repeatflag = 0 order by fromtime, id";
			curDate.add(Calendar.DATE, -1);

			// Get the entries from database
			v = new Vector();
			DbAdapter db = new DbAdapter(true);
			BoxCalendarEntryBean bean = new BoxCalendarEntryBean(null, db);
			Iterator it = bean.findBySql(sql);
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);

					v.addElement(entry);
				}
			}

			// Get the repeat entries
			sql = " where userid =" + userid + " and repeatflag = 1";
			it = bean.findBySql(sql);
			if (it != null && it.hasNext()) {
				while (it.hasNext()) {
					BoxCalendarEntryKey key = (BoxCalendarEntryKey) it.next();
					BoxCalendarEntryBean entryBean = new BoxCalendarEntryBean(
							key, db);
					entryBean.load();
					BoxCalendarEntry entry = new BoxCalendarEntry();
					entry.copyFrom(entryBean);
					if (!entry.getIsAllDay()) // only get non-allday entries
					{
						BoxCalendarRepeatBean repeatBean = new BoxCalendarRepeatBean(
								key, db);
						repeatBean.load();
						BoxCalendarRepeat repeat = new BoxCalendarRepeat();
						repeat.copyFrom(repeatBean);

						if (isRepeatInDate(entry, repeat, curDate)) {
							insertBeanToSortedVector(entry, v, curDate
									.getTimeZone());
						}
					}
				}
			}
			db.close();
			db = null;
		} catch (Exception e) {
			logger.error(e);
		}
		if (v != null && v.size() > 0) {
			if (viewerid != userid) {
				filterByViewer(viewerid, v);
			}
			return v.iterator();
		} else {
			return null;
		}
	}

	// Generate the list of entries of a day
	public Vector getColumnedEntries(int viewerid, Calendar date, int userid) {
		// Generate the empty entries first
		// Get the entries of that day from database
		// Merge two list
		Vector v = new Vector();
		Iterator it = getEntries(viewerid, date, userid);
		if (it != null && it.hasNext()) {
			while (it.hasNext()) {
				BoxCalendarEntry entry = (BoxCalendarEntry) it.next();
				insertBeanToVector(v, entry, date.getTimeZone());
			}
		}
		return v;
	}

	public BoxCalendarEntry findEntryBetween(Vector v, TimeZone tz, long from,
			long to) {
		// from and to should be in the same day, only compare the time in a day
		// here
		Calendar tmpCal = Calendar.getInstance(tz);
		tmpCal.setTime(new Date(from));
		int fromTmp = tmpCal.get(Calendar.HOUR_OF_DAY);
		tmpCal.setTime(new Date(to));
		int toTmp = tmpCal.get(Calendar.HOUR_OF_DAY);

		for (int i = 0; i < v.size(); i++) {
			BoxCalendarEntry entry = (BoxCalendarEntry) v.elementAt(i);
			if (entry != null && entry.getFromTime() != null) {
				tmpCal.setTime(entry.getFromTime());
				int tmp = tmpCal.get(Calendar.HOUR_OF_DAY);
				if (tmp >= fromTmp && tmp < toTmp) {
					return entry;
				}
			}
		}
		return null;
	}

	public long getEarliestTime(int startHour, Calendar curDate, Vector v) {
		// TODO: check the same day
		Calendar ret = Calendar.getInstance(curDate.getTimeZone());
		int retHour = startHour;
		if (v != null) {
			for (int i = 0; i < v.size(); i++) {
				Vector v1 = (Vector) v.elementAt(i);
				if (v1 != null) {
					for (int j = 0; j < v1.size(); j++) {
						BoxCalendarEntry entry = (BoxCalendarEntry) v1
								.elementAt(j);
						if (entry != null && entry.getFromTime() != null) {
							int tmpHour = entry.getFromHour(curDate
									.getTimeZone());
							if (tmpHour < retHour) {
								retHour = tmpHour;
							}
						}
					}
				}
			}
		}
		ret.setTime(curDate.getTime());
		ret.set(Calendar.HOUR_OF_DAY, retHour);
		trimForHour(ret);
		return ret.getTime().getTime();
	}

	public long getLatestTime(int endHour, Calendar curDate, Vector v) {
		// TODO: check the same day
		Calendar ret = Calendar.getInstance(curDate.getTimeZone());
		int retHour = endHour;

		if (v != null) {
			for (int i = 0; i < v.size(); i++) {
				Vector v1 = (Vector) v.elementAt(i);
				if (v1 != null) {
					for (int j = 0; j < v1.size(); j++) {
						BoxCalendarEntry entry = (BoxCalendarEntry) v1
								.elementAt(j);
						if (entry != null && entry.getFromTime() != null) {
							int tmpHour = entry.getFromHour(curDate
									.getTimeZone());
							if (tmpHour > retHour) {
								retHour = tmpHour;
							}
							if (entry.getToTime() != null) {
								tmpHour = entry
										.getToHour(curDate.getTimeZone());
								if (tmpHour > retHour) {
									retHour = tmpHour;
								}
							}
						}
					}
				}
			}
		}
		ret.setTime(curDate.getTime());
		trimForHour(ret);
		if (retHour < 24) {
			ret.set(Calendar.HOUR_OF_DAY, retHour + 1);
		} else {
			ret.set(Calendar.HOUR_OF_DAY, 24);

		}
		return ret.getTime().getTime();
	}

	public int getEntryRows(BoxCalendarEntry entry, TimeZone tz) {
		int ret = 1;
		if (entry == null) {
			return ret;
		}

		Timestamp t1 = entry.getFromTime();
		if (t1 == null) {
			return ret;
		}
		Timestamp t2 = entry.getToTime();
		if (t2 == null) {
			return ret;
		}
		if (t1.getTime() >= t2.getTime()) {
			return ret;
		}
		// TODO::
		Calendar tmp1 = Calendar.getInstance(tz);
		Calendar tmp2 = Calendar.getInstance(tz);
		tmp1.setTime(new Date(t1.getTime()));
		tmp2.setTime(new Date(t2.getTime()));
		ret = tmp2.get(Calendar.HOUR_OF_DAY) - tmp1.get(Calendar.HOUR_OF_DAY);
		if (tmp2.get(Calendar.MINUTE) > 0) {
			ret++;
		}
		if (ret == 0) {
			ret++;

		}
		return ret;
	}

	/*
	 * public String getTimeString(long curTime) {
	 * BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.SHORT_TIME_FORMAT);
	 * return BoxStatic.dateTimeFormatter.format(new java.sql.Date(curTime)); }
	 */
	public static String getWeekRangeString(Calendar date) {
		Calendar d = (Calendar) date.clone();
		BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.LONG_DATE_FORMAT);
		String s = BoxStatic.dateTimeFormatter.format(d.getTime());
		d.add(Calendar.DATE, 7);
		s += " - " + BoxStatic.dateTimeFormatter.format(d.getTime());
		return s;
	}

	public String getTimeRangeString(BoxCalendarEntry entry, TimeZone tz) {
		if (entry == null) {
			return "";
		}

		return entry.getTimeRangeString(tz);
	}

	public void getMonthStartEnd(Calendar start, Calendar end,
			Calendar currentDate) {
		start.setTime(currentDate.getTime());
		trimForDay(start);
		end.setTime(start.getTime());

		start.set(Calendar.DATE, 1);
		int dayOfWeek = start.get(Calendar.DAY_OF_WEEK) - 1;
		start.add(Calendar.DAY_OF_YEAR, -dayOfWeek);

		end.add(Calendar.MONTH, 1);
		end.set(Calendar.DATE, 1);
		end.add(Calendar.DATE, -1);
		dayOfWeek = end.get(Calendar.DAY_OF_WEEK);
		end.add(Calendar.DAY_OF_YEAR, (7 - dayOfWeek));
	}

	protected void insertBeanToVector(Vector v, BoxCalendarEntry entry,
			TimeZone tz) {
		for (int i = 0; i < v.size(); i++) {
			Vector v1 = (Vector) v.elementAt(i);
			if (v1 != null) {
				if (isConflict(v1, entry, tz) == false) {
					v1.addElement(entry);
					return;
				}
			}
		}

		Vector v2 = new Vector(4);
		v2.addElement(entry);
		v.addElement(v2);
	}

	protected boolean isConflict(Vector v, BoxCalendarEntry entry, TimeZone tz) {
		if (entry.getFromTime() == null) {
			return false;
		}
		int fromHour = entry.getFromHour(tz);
		int toHour = entry.getToHour(tz);

		for (int i = 0; i < v.size(); i++) {
			BoxCalendarEntry tmp = (BoxCalendarEntry) v.elementAt(i);
			int tmpFromHour = tmp.getFromHour(tz);
			int tmpToHour = tmp.getToHour(tz);
			if ((fromHour >= tmpFromHour && fromHour < tmpToHour)
					|| (tmpFromHour >= fromHour && tmpFromHour < toHour)) {
				return true;
			}
		}
		return false;
	}

	protected void insertBeanToSortedVector(BoxCalendarEntry entry, Vector v,
			TimeZone tz) {
		Calendar beanTime = Calendar.getInstance(tz);
		Calendar tmpTime = Calendar.getInstance(tz);
		beanTime.setTime(entry.getFromTime());
		for (int i = 0; i < v.size(); i++) {
			BoxCalendarEntry tmp = (BoxCalendarEntry) v.elementAt(i);
			tmpTime.setTime(tmp.getFromTime());
			if (compareByTime(tmpTime, beanTime) >= 0) {
				v.insertElementAt(entry, i);
				return;
			}
		}
		// if not inserted, append to end
		v.addElement(entry);
	}

	protected boolean isRepeatInDate(BoxCalendarEntry entry,
			BoxCalendarRepeat repeat, Calendar curDate) {
		// only check the date here
		trimForDay(curDate);
		Calendar fromTime = Calendar.getInstance(curDate.getTimeZone());
		fromTime.setTime(entry.getFromTime());
		trimForDay(fromTime);

		Calendar untilTime = null;
		if (repeat.getUntilTime() != null) {
			untilTime = Calendar.getInstance(curDate.getTimeZone());
			untilTime.setTime(repeat.getUntilTime());
		}
		int fromYear = fromTime.get(Calendar.YEAR);
		int curYear = curDate.get(Calendar.YEAR);
		int fromDay = fromTime.get(Calendar.DAY_OF_YEAR);
		int curDay = curDate.get(Calendar.DAY_OF_YEAR);
		if (fromYear > curYear) {
			return false;
		} else if ((fromYear == curYear) && (fromDay > curDay)) {
			return false;
		}

		if (untilTime != null) {
			if (curYear > untilTime.get(Calendar.YEAR)) {
				return false;
			} else if ((curYear == untilTime.get(Calendar.YEAR))
					&& (curDay > untilTime.get(Calendar.DAY_OF_YEAR))) {
				return false;
			}
		}

		switch (repeat.getType()) {
		case BoxCalendarRepeatBean.BY_WEEK: {
			// System.out.println(curDate.getTime());
			int weekdays = repeat.getDayOfWeek();
			int curDayOfWeek = getDayOfWeekBit(curDate);
			if ((weekdays & curDayOfWeek) == 0) {
				return false;
			}
			int every = repeat.getEvery();
			long diff = fromTime.get(Calendar.DAY_OF_WEEK) - 1;
			if (fromYear == curYear) {
				diff += (curDay - fromDay);
			} else {
				diff += ((curDate.getTime().getTime() / (1000 * 60 * 60 * 24)) - (fromTime
						.getTime().getTime() / (1000 * 60 * 60 * 24)));
			}
			diff = diff / 7;
			if ((diff % every) == 0) {
				return true;
			} else {
				return false;
			}
		}
		case BoxCalendarRepeatBean.BY_WEEK_OF_MONTH: {
			// first check the same weekday
			int weekdays = repeat.getDayOfWeek();
			int curDayOfWeek = getDayOfWeekBit(curDate);
			if ((weekdays & curDayOfWeek) == 0) {
				return false;
			}
			// then check every n months
			long diff = curDate.get(Calendar.MONTH) + 12 * (curYear - fromYear)
					- fromTime.get(Calendar.MONTH);
			int every = repeat.getEvery();
			if ((diff % every) != 0) {
				return false;
			}

			// then which week
			every = repeat.getWhichDay();
			diff = curDate.get(Calendar.MONTH);
			if (every < 5) {
				curDate.add(Calendar.DAY_OF_YEAR, -7 * every);
				if (diff == curDate.get(Calendar.MONTH)) {
					return false;
				}
				for (int i = 0; i < every; i++) {
					curDate.add(Calendar.DAY_OF_YEAR, 7);
					if (diff != curDate.get(Calendar.MONTH)) {
						return false;
					}
				}
				return true;
			} else // last week
			{
				// Add 7 days, should different month
				curDate.add(Calendar.DAY_OF_YEAR, 7);
				every = curDate.get(Calendar.MONTH);
				if (diff != every) {
					return true;
				}
			}
			return false;
		}
		case BoxCalendarRepeatBean.BY_MONTH: {
			if (fromTime.get(Calendar.DAY_OF_MONTH) == curDate
					.get(Calendar.DAY_OF_MONTH)) {
				return true;
			}
			return false;
		}
		case BoxCalendarRepeatBean.BY_YEAR: {
			if (fromDay == curDay) {
				return true;
			}
			return false;
		}
		}
		return false;
	}

	protected int compareByTime(Calendar time1, Calendar time2) {
		return (int) (time1.getTime().getTime() % (24 * 60 * 60 * 1000) - time2
				.getTime().getTime()
				% (24 * 60 * 60 * 1000));
	}

	public void trimForDay(Calendar curDate) {
		// adjust time to day
		curDate.set(Calendar.MILLISECOND, 0);
		curDate.set(Calendar.SECOND, 0);
		curDate.set(Calendar.MINUTE, 0);
		curDate.set(Calendar.HOUR_OF_DAY, 0);
	}

	public void trimForHour(Calendar curDate) {
		// adjust time to hour
		curDate.set(Calendar.MILLISECOND, 0);
		curDate.set(Calendar.SECOND, 0);
		curDate.set(Calendar.MINUTE, 0);
	}

	public int getDayOfWeekBit(int weekday) {
		switch (weekday) {
		case Calendar.SUNDAY:
			return 1;
		case Calendar.MONDAY:
			return 2;
		case Calendar.TUESDAY:
			return 4;
		case Calendar.WEDNESDAY:
			return 8;
		case Calendar.THURSDAY:
			return 16;
		case Calendar.FRIDAY:
			return 32;
		case Calendar.SATURDAY:
			return 64;
		}
		return 1;
	}

	public int getDayOfWeekBit(Calendar curDate) {
		return getDayOfWeekBit(curDate.get(Calendar.DAY_OF_WEEK));
	}

	protected void adjustFromToTime(Calendar fromTime, Calendar toTime,
			BoxCalendarEntry entry) {
		// align the time to hour

		fromTime.setTime(entry.getFromTime());
		if (entry.getToTime() == null) {
			toTime.setTime(entry.getFromTime());
		} else {
			toTime.setTime(entry.getToTime());
		}
		trimForHour(fromTime);
		toTime.set(Calendar.MINUTE, 59);
		toTime.set(Calendar.SECOND, 59);
		toTime.set(Calendar.MILLISECOND, 999);
	}

	/**
	 * getAllEntries
	 * 
	 * @param userid
	 *            int
	 * @param listType
	 *            int
	 * @param category
	 *            String
	 * @param SORTBY
	 *            String
	 * @param sortAsc
	 *            boolean
	 * @param from
	 *            int
	 * @param limit
	 *            int
	 * @return Collection
	 */
	public Collection getAllEntries(int viewerid, int userid, int listType,
			String category, String SORTBY, boolean sortAsc, int from, int limit) {
		StringBuffer sql = new StringBuffer();
		sql.append(" where userid=" + userid);

		if (category != null && category.length() > 0) {
			sql.append(" and category = '"
					+ StringUtil.replace(category, "\'", "\'\'") + "'");
		}
		if (listType == BOXCALENDAR_LIST_UPCOMING
				|| listType == BOXCALENDAR_LIST_PAST) {
			BoxStatic.dateTimeFormatter
					.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
			Calendar today = Calendar.getInstance();
			trimForDay(today);
			String strDate = BoxStatic.dateTimeFormatter
					.format(today.getTime());
			if (listType == BOXCALENDAR_LIST_UPCOMING) {
				sql.append(" and fromtime>='" + strDate + "'");
			} else if (listType == BOXCALENDAR_LIST_PAST) {
				sql.append(" and fromtime<'" + strDate + "'");
			}
		}

		return search(viewerid, userid, sql.toString(), SORTBY, sortAsc, from,
				limit);
	}

	/**
	 * filterByViewer
	 * 
	 * @param viewerID
	 *            int
	 * @param v
	 *            Vector
	 */
	protected void filterByViewer(int viewerid, Vector v) {
		if (v != null && v.size() > 0 && viewerid >= 0) {
			Collection removeCol = new ArrayList();
			Iterator it = v.iterator();
			while (it.hasNext()) {
				BoxCalendarEntry entry = (BoxCalendarEntry) it.next();
				if (entry.getUserID() != viewerid
						&& entry.getSharing() == BOXCALENDAR_PRIVATE) {
					removeCol.add(entry);
				}
			}
			if (removeCol.size() > 0) {
				v.removeAll(removeCol);
			}
		}
	}

	/**
	 * filterByViewer
	 * 
	 * @param viewerID
	 *            int
	 * @param col
	 *            Collection
	 */
	protected void filterByViewer(int viewerid, Collection col) {
		if (col != null && col.size() > 0 && viewerid >= 0) {
			Collection removeCol = new ArrayList();
			Iterator it = col.iterator();
			while (it.hasNext()) {
				BoxCalendarEntry entry = (BoxCalendarEntry) it.next();
				if (entry.getUserID() != viewerid
						&& entry.getSharing() == BOXCALENDAR_PRIVATE) {
					removeCol.add(entry);
				}
			}
			if (removeCol.size() > 0) {
				col.removeAll(removeCol);
			}
		}
	}

	/**
	 * clearCalendar
	 * 
	 * @param userid
	 *            int
	 */
	public void clearCalendar(int userid) throws Exception {
		Collection col = getAllEntries(userid, userid, BOXCALENDAR_LIST_ALL,
				null, "fromtime", true, -1, -1);
		Iterator it = col.iterator();
		if (it != null && it.hasNext()) {
			while (it.hasNext()) {
				BoxCalendarEntry entry = (BoxCalendarEntry) it.next();
				remove(entry);
			}
		}
	}
}
